﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace UtilZ.Dotnet.Ex.Model
{
#pragma warning disable 8500

    /// <summary>
    /// 内存池管理器
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    unsafe public class MemoryPoolZManager<T> : IDisposable where T : struct
    {
        private bool _dsposabled = false;
        private readonly object _lock = new object();

        private readonly bool _enableDefaultRent;
        private readonly BufferSearchAlgorithmType _searchAlgorithmType;
        private readonly MemoryPoolZ<T>[] _memoryPools;

        private readonly List<MemoryPoolZManagerBlock<T>> _appliedBlocks = new List<MemoryPoolZManagerBlock<T>>();
        private readonly ConcurrentBag<MemoryPoolZManagerBlock<T>> _availableRentBlocks = new ConcurrentBag<MemoryPoolZManagerBlock<T>>();


        /// <summary>
        /// 是否启用默认租借选项,启用后当租借数据长度超出预定义范围使用默认租借,禁用则抛出异常.[true:启用;false:禁用]
        /// </summary>
        public bool EnableDefaultRent
        {
            get
            {
                return this._enableDefaultRent;
            }
        }

        /// <summary>
        /// 搜索算法类型,选项集合中的项少于5项时推荐顺序查找,多于5项时建议二分查找
        /// </summary>
        public BufferSearchAlgorithmType SearchAlgorithmType
        {
            get
            {
                return this._searchAlgorithmType;
            }
        }

        /// <summary>
        /// 默认租借项数
        /// </summary>
        public int DefaultRentCount
        {
            get
            {
                lock (this._lock)
                {
                    return this._appliedBlocks.Count;
                }
            }
        }

        /// <summary>
        /// 默认租借最长数据长度
        /// </summary>
        public int DefaultRentMaxDataLength
        {
            get
            {
                lock (this._lock)
                {
                    if (this._dsposabled)
                    {
                        return 0;
                    }

                    if (this._appliedBlocks.Count <= 0)
                    {
                        return 0;
                    }

                    return this._appliedBlocks.Max(t => { return t.Capcity; });
                }
            }
        }

        /// <summary>
        /// 获取所有内存租借信息,最后一项为默认租借信息
        /// </summary>
        public MemoryRentInfo[] RentInfos
        {
            get
            {
                lock (this._lock)
                {
                    MemoryRentInfo[] rentInfos;
                    if (this._enableDefaultRent)
                    {
                        rentInfos = new MemoryRentInfo[this._memoryPools.Length + 1];
                        rentInfos[rentInfos.Length - 1] = new MemoryRentInfo(this.DefaultRentMaxDataLength, this._appliedBlocks.Count, this._appliedBlocks.Count);
                    }
                    else
                    {
                        rentInfos = new MemoryRentInfo[this._memoryPools.Length];
                    }

                    for (int i = 0; i < this._memoryPools.Length; i++)
                    {
                        rentInfos[i] = this._memoryPools[i].RentInfo;
                    }

                    return rentInfos;
                }
            }
        }



        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="opions">选项集合</param>
        /// <param name="searchAlgorithmType">搜索算法类型,选项集合中的项少于5项时推荐顺序查找,多于5项时建议二分查找</param>
        /// <param name="enableDefaultRent">是否启用默认租借选项,启用后当租借数据长度超出预定义范围使用默认租借,禁用则抛出异常.[true:启用;false:禁用]</param>
        public MemoryPoolZManager(IEnumerable<MemoryPoolZOption> opions, BufferSearchAlgorithmType searchAlgorithmType = BufferSearchAlgorithmType.Binary, bool enableDefaultRent = true)
        {
            if (opions == null)
            {
                throw new ArgumentNullException(nameof(opions));
            }

            if (opions.Count() <= 0)
            {
                throw new ArgumentException("初始化化选项集合不能为空.");
            }

            this._searchAlgorithmType = searchAlgorithmType;
            this._enableDefaultRent = enableDefaultRent;

            opions = opions.Distinct().OrderBy(t => { return t.BlockAlignedSize; });
            this._memoryPools = new MemoryPoolZ<T>[opions.Count()];
            for (int i = 0; i < this._memoryPools.Length; i++)
            {
                var opion = opions.ElementAt(i);
                this._memoryPools[i] = new MemoryPoolZ<T>(opion.BlockAlignedSize, opion.IncrementBlocks);
            }
        }

        /// <summary>
        /// 租借指定长度的内存块
        /// </summary>
        /// <param name="dataLength">数据长度</param>
        /// <returns>租借到的内存块</returns>
        public IMemoryBlock<T> Rent(int dataLength)
        {
            if (this._searchAlgorithmType == BufferSearchAlgorithmType.Sequence)
            {
                return this.SequenceRent(this._memoryPools, dataLength);
            }
            else
            {
                return this.BinaryRent(this._memoryPools, dataLength);
            }
        }

        private IMemoryBlock<T> SequenceRent(MemoryPoolZ<T>[] memoryPools, int dataLength)
        {
            foreach (var memoryPool in memoryPools)
            {
                if (memoryPool.BlockAlignedSize >= dataLength)
                {
                    return memoryPool.Rent(dataLength);
                }
            }

            if (this._enableDefaultRent)
            {
                return this.DefaultRent(dataLength);
            }
            else
            {
                throw new InvalidOperationException($"租借数据长度\"{dataLength}\"不在初始化范围内,且未启用默认租借选项.");
            }
        }

        private IMemoryBlock<T> BinaryRent(MemoryPoolZ<T>[] memoryPools, int dataLength)
        {
            int left = 0, right = memoryPools.Length - 1, midIndex;
            MemoryPoolZ<T> memoryPool;

            while (left <= right)
            {
                midIndex = left + ((right - left) >> 1);
                memoryPool = memoryPools[midIndex];

                if (memoryPool.BlockAlignedSize >= dataLength)
                {
                    return memoryPool.Rent(dataLength);
                }
                else if (memoryPool.BlockAlignedSize < dataLength)
                {
                    left = midIndex + 1;
                }
                else
                {
                    right = midIndex - 1;
                }
            }

            if (this._enableDefaultRent)
            {
                return this.DefaultRent(dataLength);
            }
            else
            {
                throw new InvalidOperationException($"租借数据长度\"{dataLength}\"不在初始化范围内,且未启用默认租借选项.");
            }
        }

        private IMemoryBlock<T> DefaultRent(int dataLength)
        {
            if (this._dsposabled)
            {
                throw new ObjectDisposedException(nameof(MemoryPoolZ<T>), "当前对象已释放");
            }

            MemoryPoolZManagerBlock<T> block;
            if (this._availableRentBlocks.TryTake(out block))
            {
                block.SetLength(dataLength);
                return block;
            }

            lock (this._lock)
            {
                if (this._dsposabled)
                {
                    throw new ObjectDisposedException(nameof(MemoryPoolZ<T>), "当前对象已释放");
                }

                if (this._availableRentBlocks.TryTake(out block))
                {
                    block.SetLength(dataLength);
                    return block;
                }

                var newBlock = new MemoryPoolZManagerBlock<T>(this);
                this._appliedBlocks.Add(newBlock);
                newBlock.SetLength(dataLength);
                return newBlock;
            }
        }


        /// <summary>
        /// 返回租借的内存块
        /// </summary>
        /// <param name="block">用于返回的租借的内存块</param>
        internal void Return(IMemoryBlock<T> block)
        {
            this._availableRentBlocks.Add((MemoryPoolZManagerBlock<T>)block);
        }

        /// <summary>
        /// 释放资源,释放后不能再使用已租借的内存块
        /// </summary>
        public void Dispose()
        {
            lock (this._lock)
            {
                if (this._dsposabled)
                {
                    return;
                }
                this._dsposabled = true;

                for (int i = 0; i < this._memoryPools.Length; i++)
                {
                    this._memoryPools[i].Dispose();
                    this._memoryPools[i] = null;
                }

                lock (this._lock)
                {
                    foreach (var block in this._appliedBlocks)
                    {
                        block.Free();
                    }
                    this._appliedBlocks.Clear();
                }
            }
        }
    }

    /// <summary>
    /// 内部实现内存块
    /// </summary>
    unsafe internal class MemoryPoolZManagerBlock<T> : IMemoryBlock<T> where T : struct
    {
        private T* _ptr = null;
        private int _capcity = 0;
        private int _length = 0;
        private readonly MemoryPoolZManager<T> _owner;


        internal int Capcity
        {
            get { return this._capcity; }
        }


        public MemoryPoolZManagerBlock(MemoryPoolZManager<T> ownerPool)
        {
            this._owner = ownerPool;
        }

        internal void SetLength(int dataLength)
        {
            if (dataLength < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(dataLength), $"租借数据长度\"{dataLength}\"无效,长度必须大于或等于0");
            }

            if (dataLength > this._capcity)
            {
                int newCapcity = dataLength;
                if (this._ptr == null)
                {
                    this._ptr = (T*)NativeMemory.Alloc((nuint)(sizeof(T) * newCapcity));
                }
                else
                {
                    void* newPtr = NativeMemory.Realloc(this._ptr, (nuint)(sizeof(T) * newCapcity));
                    if (newPtr == null)
                    {
                        throw new InvalidOperationException($"系统没有足够的内存供申请,当前申请内存大小\"{sizeof(T) * dataLength}\"字节.");
                    }
                    else
                    {
                        this._ptr = (T*)newPtr;
                    }
                }
                this._capcity = newCapcity;
            }
            this._length = dataLength;
        }

        /// <summary>
        /// 数据指针
        /// </summary>
        public T* DataPtr
        {
            get { return this._ptr; }
        }

        /// <summary>
        /// 数据长度
        /// </summary>
        public int Length
        {
            get { return this._length; }
        }

        /// <summary>
        /// Span数据
        /// </summary>
        public Span<T> Span
        {
            get
            {
                return new Span<T>(this._ptr, this._length);
            }
        }

        /// <summary>
        /// SpanZ数据
        /// </summary>
        public SpanZ<T> SpanZ
        {
            get
            {
                return new SpanZ<T>(this._ptr, this._length);
            }
        }

        /// <summary>
        /// 获取或设置值索引,无越界检查(注:长度超过30000的循环不建议使用索引,速度会低于Span和SpanZ)
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T this[int index]
        {
            get { return this._ptr[index]; }
            set { this._ptr[index] = value; }
        }

        /// <summary>
        /// 将当前对象返还给所属内存池
        /// </summary>
        public void Dispose()
        {
            this._owner.Return(this);
        }

        internal void Free()
        {
            if (this._ptr != null)
            {
                NativeMemory.Free(this._ptr);
                this._ptr = null;
                this._length = 0;
                this._capcity = 0;
            }
        }

    }

    /// <summary>
    /// 内存池
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    unsafe public class MemoryPoolZ<T> : IDisposable where T : struct
    {
        /// <summary>
        /// 内存块对齐大小
        /// </summary>
        public readonly int BlockAlignedSize;

        /// <summary>
        /// 内存扩增时的单次增加内存块数
        /// </summary>
        public readonly int IncrementBlocks;

        private readonly List<UnmanagedMemoryPointer<T>> _appliedPointers = new List<UnmanagedMemoryPointer<T>>();
        private readonly ConcurrentBag<MemoryBlock<T>> _availableRentBlocks = new ConcurrentBag<MemoryBlock<T>>();
        private readonly object _lock = new object();
        private bool _dsposabled = false;


        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="blockAlignedSize">内存块对齐大小</param>
        /// <param name="incrementBlocks">内存扩增时的单次增加内存块数</param>
        public MemoryPoolZ(int blockAlignedSize, int incrementBlocks)
        {
            this.BlockAlignedSize = blockAlignedSize;
            this.IncrementBlocks = incrementBlocks;
        }


        /// <summary>
        /// 获取内存租借信息
        /// </summary>
        public MemoryRentInfo RentInfo
        {
            get
            {
                lock (this._lock)
                {
                    return new MemoryRentInfo(this.BlockAlignedSize, this.IncrementBlocks, this._appliedPointers.Count);
                }
            }
        }

        /// <summary>
        /// 租借指定长度的内存块
        /// </summary>
        /// <param name="length">数据长度</param>
        /// <returns>租借到的内存块</returns>
        public IMemoryBlock<T> Rent(int length)
        {
            if (this._dsposabled)
            {
                throw new ObjectDisposedException(nameof(MemoryPoolZ<T>), "当前对象已释放");
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"租借数据长度\"{length}\"无效,长度必须大于或等于0");
            }

            MemoryBlock<T> block;
            if (this._availableRentBlocks.TryTake(out block))
            {
                block.SetLength(length);
                return block;
            }

            lock (this._lock)
            {
                while (true)
                {
                    if (this._dsposabled)
                    {
                        throw new ObjectDisposedException(nameof(MemoryPoolZ<T>), "当前对象已释放");
                    }

                    this.AddMemoryPointer();
                    if (this._availableRentBlocks.TryTake(out block))
                    {
                        block.SetLength(length);
                        return block;
                    }
                }
            }
        }

        private void AddMemoryPointer()
        {
            var pointer = new UnmanagedMemoryPointer<T>((long)this.BlockAlignedSize * this.IncrementBlocks);
            this._appliedPointers.Add(pointer);

            T* ptr = pointer.DataPtr;
            for (int i = 0; i < this.IncrementBlocks; i++)
            {
                this._availableRentBlocks.Add(new MemoryBlock<T>(ptr, this.BlockAlignedSize, this));
                ptr += this.BlockAlignedSize;
            }
        }

        /// <summary>
        /// 返回租借的内存块
        /// </summary>
        /// <param name="block">用于返回的租借的内存块</param>
        internal void Return(IMemoryBlock<T> block)
        {
            this._availableRentBlocks.Add((MemoryBlock<T>)block);
        }

        /// <summary>
        /// 释放资源,释放后不能再使用已租借的内存块
        /// </summary>
        public void Dispose()
        {
            lock (this._lock)
            {
                if (this._dsposabled)
                {
                    return;
                }
                this._dsposabled = true;

                foreach (var item in this._appliedPointers)
                {
                    item.Dispose();
                }
                this._appliedPointers.Clear();
                this._availableRentBlocks.Clear();
            }
        }

    }

    /// <summary>
    /// 内存租借信息
    /// </summary>
    public class MemoryRentInfo
    {
        /// <summary>
        /// 内存块对齐大小
        /// </summary>
        public readonly int BlockAlignedSize;

        /// <summary>
        /// 内存扩增时的单次增加内存块数
        /// </summary>
        public readonly int IncrementBlocks;

        /// <summary>
        /// 已申请的内存块数
        /// </summary>
        public readonly int PointerCount;


        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="blockAlignedSize">内存块对齐大小</param>
        /// <param name="incrementBlocks">内存扩增时的单次增加内存块数</param>
        /// <param name="pointerCount">已申请的内存块数</param>
        public MemoryRentInfo(int blockAlignedSize, int incrementBlocks, int pointerCount)
        {
            this.BlockAlignedSize = blockAlignedSize;
            this.IncrementBlocks = incrementBlocks;
            this.PointerCount = pointerCount;
        }

    }

    /// <summary>
    /// 内部实现内存块
    /// </summary>
    unsafe internal class MemoryBlock<T> : IMemoryBlock<T> where T : struct
    {
        private readonly T* _ptr;
        private readonly int _capcity;
        private int _length;
        private readonly MemoryPoolZ<T> _owner;

        public MemoryBlock(T* ptr, int length, MemoryPoolZ<T> ownerPool)
        {
            this._ptr = ptr;
            this._capcity = length;
            this._owner = ownerPool;
        }

        internal void SetLength(int dataLength)
        {
            if (dataLength > this._capcity)
            {
                throw new ArgumentOutOfRangeException(nameof(dataLength), $"数据长度{dataLength}超出容量范围{this._capcity}.");
            }

            this._length = dataLength;
        }

        /// <summary>
        /// 数据指针
        /// </summary>
        public T* DataPtr
        {
            get { return this._ptr; }
        }

        /// <summary>
        /// 数据长度
        /// </summary>
        public int Length
        {
            get { return this._length; }
        }

        /// <summary>
        /// Span数据
        /// </summary>
        public Span<T> Span
        {
            get
            {
                return new Span<T>(this._ptr, this._length);
            }
        }

        /// <summary>
        /// SpanZ数据
        /// </summary>
        public SpanZ<T> SpanZ
        {
            get
            {
                return new SpanZ<T>(this._ptr, this._length);
            }
        }

        /// <summary>
        /// 获取或设置值索引,无越界检查(注:长度超过30000的循环不建议使用索引,速度会低于Span和SpanZ)
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T this[int index]
        {
            get { return this._ptr[index]; }
            set { this._ptr[index] = value; }
        }

        /// <summary>
        /// 将当前对象返还给所属内存池
        /// </summary>
        public void Dispose()
        {
            this._owner.Return(this);
        }

    }

    /// <summary>
    /// 内存块
    /// </summary>
    /// <typeparam name="T"></typeparam>
    unsafe public interface IMemoryBlock<T> : IDisposable where T : struct
    {
        /// <summary>
        /// 数据指针
        /// </summary>
        T* DataPtr { get; }

        /// <summary>
        /// 数据长度
        /// </summary>
        int Length { get; }

        /// <summary>
        /// Span数据
        /// </summary>
        Span<T> Span { get; }

        /// <summary>
        /// SpanZ数据
        /// </summary>
        SpanZ<T> SpanZ { get; }

        /// <summary>
        /// 获取或设置值索引,无越界检查(注:长度超过30000的循环不建议使用索引,速度会低于Span和SpanZ)
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        T this[int index] { get; set; }
    }

    /// <summary>
    /// 内存池初始化选项
    /// </summary>
    public class MemoryPoolZOption
    {
        /// <summary>
        /// 内存块对齐大小
        /// </summary>
        public int BlockAlignedSize { get; private set; }

        /// <summary>
        /// 内存扩增时的单次增加内存块数
        /// </summary>
        public int IncrementBlocks { get; private set; }




        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="blockAlignedSize"></param>
        /// <param name="incrementBlocks"></param>
        public MemoryPoolZOption(int blockAlignedSize, int incrementBlocks)
        {
            this.BlockAlignedSize = blockAlignedSize;
            this.IncrementBlocks = incrementBlocks;
        }

        /// <summary>
        /// 重写GetHashCode
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.BlockAlignedSize;
        }

        /// <summary>
        /// 重写Equals
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (obj is MemoryPoolZOption)
            {
                var exObj = (MemoryPoolZOption)obj;
                return exObj.BlockAlignedSize == this.BlockAlignedSize && this.IncrementBlocks == exObj.IncrementBlocks;
            }
            else
            {
                return false;
            }

        }
    }

}
